package organizer.hb;

import java.util.ArrayList;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import organizer.FileMetadata;
import organizer.IFileMetadata;
import organizer.MetadataComparator;
import organizer.QuickSort;
import organizer.QuickSort.IValueProvider;

/**
 * Manipulate the derby database.
 * @author dawong
 */
public class MetadataDao {
    
    private Session session;

    public MetadataDao(Session session) {
        this.session = session;
    }

    public void clear() {
        session.createQuery("update FileMetadataEntity set sortNumber = null")
                .executeUpdate();
    }

    public void add(IFileMetadata metadata) {
        FileMetadataEntity entity = (FileMetadataEntity) session.get(FileMetadataEntity.class, metadata.getPath());
        if (entity == null) {
            entity = new FileMetadataEntity(metadata);
        }
        entity.setSortNumber(0L);
        session.saveOrUpdate(entity);
    }
    
    public void sort(MetadataComparator comp) {
        final List<String> idList = session.createQuery(
                "select m1.path from FileMetadataEntity m1, FileMetadataEntity m2"
                + " where m1.sortNumber is not null and m1.path != m2.path"
                + " and m1.length = m2.length order by m1.length").list();
        IValueProvider<IFileMetadata> valueProvider = new IValueProvider() {

            public IFileMetadata getValue(int i) {
                return (IFileMetadata) session.get(FileMetadataEntity.class, idList.get(i));
            }
        };
        QuickSort.INSTANCE.quicksort(valueProvider, comp, idList);
        long sortCount = 1;
        for (String path : idList) {
            FileMetadataEntity entity = (FileMetadataEntity) session.get(FileMetadataEntity.class, path);
            entity.setSortNumber(sortCount++);
            session.update(entity);
        }
    }
    
    public int size() {
        return ((Number) session.createCriteria(FileMetadataEntity.class)
                .setProjection(Projections.count("path"))
                .add(Restrictions.isNotNull("sortNumber"))
                .uniqueResult()).intValue();
    }

    public IFileMetadata getSorted(int i) {
        List<FileMetadataEntity> results = session.createCriteria(FileMetadataEntity.class)
                .add(Restrictions.eq("sortNumber", (long)i+1))
                .list();
        FileMetadata ret = null;
        if (results.size() > 0) {
            FileMetadataEntity entity = results.get(0);
            ret = new FileMetadata(entity);
        }
        return ret;
    }

    public IFileMetadata getMetadata(String path) {
        IFileMetadata ret = null;
        List<FileMetadataEntity> results = session
                .createCriteria(FileMetadataEntity.class)
                .add(Restrictions.eq("path", path))
                .list();
        if (results.size() > 0) {
            FileMetadataEntity entity = results.get(0);
            ret = new FileMetadata(entity);
        }
        return ret;
    }

    public List<IFileMetadata> getSorted(int start, int length) {
        List<FileMetadataEntity> results = session
                .createCriteria(FileMetadataEntity.class)
                .add(Restrictions.isNotNull("sortNumber"))
                .addOrder(Order.asc("path"))
                .setFirstResult(start)
                .setMaxResults(length)
                .list();
        List<IFileMetadata> ret = new ArrayList<IFileMetadata>();
        for (FileMetadataEntity entity : results) {
            ret.add(new FileMetadata(entity));
        }
        return ret;
    }

    public void update(IFileMetadata metadata) {
        FileMetadataEntity result = (FileMetadataEntity) session
                .createCriteria(FileMetadataEntity.class)
                .add(Restrictions.eq("path", metadata.getPath()))
                .uniqueResult();
        if (result != null) {
            result.setModifiedDate(metadata.getModifiedDate());
            result.setLength(metadata.getLength());
            result.setContentHash(metadata.getContentHash());
            session.update(result);
        }
    }

    public int getNumberOfSameLengthFiles() {
        return ((Number) session.createCriteria(FileMetadataEntity.class)
                .setProjection(Projections.count("path"))
                .add(Restrictions.ge("sortNumber", 1l))
                .uniqueResult()).intValue();
    }

    public List<Long> getLengthGroups() {
        return session.createQuery(
                "select distinct m1.length from FileMetadataEntity m1, FileMetadataEntity m2"
                + " where m1.sortNumber >=1 and m1.path != m2.path"
                + " and m1.length = m2.length order by m1.length").list();
    }

    public int getNumberOfFilesOfLength(long length) {
        return ((Number)session.createCriteria(FileMetadataEntity.class)
                .setProjection(Projections.count("path"))
                .add(Restrictions.eq("length", length))
                .add(Restrictions.ge("sortNumber", 1l))
                .uniqueResult()).intValue();
    }

    public List<IFileMetadata> getFilesOfLength(long length, int startIndex, int maxResults) {
        return session.createCriteria(FileMetadataEntity.class)
                .add(Restrictions.eq("length", length))
                .add(Restrictions.ge("sortNumber", 1l))
                .setFirstResult(startIndex)
                .setMaxResults(maxResults)
                .list();
    }

    public List<IFileMetadata> getFilesOfHash(String hash) {
        return session.createCriteria(FileMetadataEntity.class)
                .add(Restrictions.eq("contentHash", hash))
                .add(Restrictions.ge("sortNumber", 1l))
                .list();
    }
    
    public int getHashedFileCount() {
        return ((Number) session.createCriteria(FileMetadataEntity.class)
                .setProjection(Projections.count("path"))
                .add(Restrictions.isNotNull("sortNumber"))
                .add(Restrictions.isNotNull("contentHash"))
                .uniqueResult()).intValue();
    }

    public int getTotalFileCount() {
        return ((Number) session.createCriteria(FileMetadataEntity.class)
                .setProjection(Projections.count("path"))
                .uniqueResult()).intValue();
    }

}
 